import { AudioConfig } from "@/Util/Constant";
import { Broadcaster, DataChange, EventType, Listener } from "@/game/broadcaster/Broadcaster";
import Hlddz from "@/game/model/hlddz";
import { DoubleMsgType, DoubleType, GameStatus, OutCardType, SummaryObjType } from "@/share/commonType";
import { audioBgmConfig, audioPlayerVoiceConfig } from "./audioConfig";
import { Util } from "@/Util";
import eventBus from "@/Util/EventBus";

const baseUrl = '/MusicEx/'

class GameAudioCache { // 缓存加载过的音频
    static instance: GameAudioCache
    audioMap: Map<string, HTMLAudioElement> = new Map();
    setAudio(name: string, audio: HTMLAudioElement) {
        this.audioMap.set(name, audio)
    }
    getAudio(name: string): HTMLAudioElement {
        return this.audioMap.get(name)
    }
    hasAudio(name: string): boolean {
        return this.audioMap.has(name)
    }
    static getInstance() {
        if (!this.instance) {
            this.instance = new GameAudioCache()
        }
        return this.instance
    }
}

export class GameAudio extends Listener {
    model: Hlddz = Hlddz.getInstance()
    gameAudioCache: GameAudioCache = GameAudioCache.getInstance();
    bgm: HTMLAudioElement;
    playerVoice: HTMLAudioElement;
    doubleVoice: HTMLAudioElement; // 加倍音效
    specialVoice1: HTMLAudioElement; // 特殊音效播放器1  出牌特效声 
    specialVoice2: HTMLAudioElement; // 特殊音效播放器2  发牌出牌声
    specialVoice3: HTMLAudioElement; // 特殊音效播放器3  警告声
    bgmName: string;
    container: HTMLDivElement = document.getElementById('gameAudio') as HTMLDivElement;
    constructor() {
        super()
        this.addListener(this.model)
        this.loadPlayerAudio()
        eventBus.on('timerAlert', this.timerAlert)
    }
    async loadBgmAudio(cb: any) {
        let noCaches: AudioConfig[] = []
        audioBgmConfig.forEach((item) => {
            if (!this.gameAudioCache.hasAudio(item.name)) {
                noCaches.push(item)
            }
        })
        let total = noCaches.length
        let index = 0
        for (let i = 0; i < noCaches.length; i++) {
            await new Promise((res) => {
                this.cresteAudio(noCaches[i], true, () => {
                    cb(++index / total)
                    res('')
                });
            })
        }
    }

    loadPlayerAudio() {
        let noCaches: AudioConfig[] = []
        audioPlayerVoiceConfig.forEach((item) => {
            if (!this.gameAudioCache.hasAudio(item.name)) {
                noCaches.push(item)
            }
        })
        for (let i = 0; i < noCaches.length; i++) {
            this.cresteAudio(noCaches[i], false, () => { });
        }
    }
    cresteAudio(config: AudioConfig, loop: boolean = false, callback: () => void) {
        let audio = new Audio(baseUrl + config.src)
        audio.loop = loop;
        audio.oncanplaythrough = callback
        this.gameAudioCache.setAudio(config.name, audio)
    }

    changeBgm(name: string) {
        let voice = this.gameAudioCache.getAudio(name)
        if (voice) {
            this.bgm && this.bgm.pause()
            this.bgm = voice
            this.bgm.currentTime = 0; // 从头播放
            this.bgm.play()
        }
    }
    changeDoubleVoice(name: string) {
        let voice = this.gameAudioCache.getAudio(name)
        if (voice) {
            this.doubleVoice = voice
            this.doubleVoice.play()
        }
    }
    changeSpecialVoice(name: string, specialVoice: HTMLAudioElement) {
        let voice = this.gameAudioCache.getAudio(name)
        if (voice) {
            specialVoice = voice
            specialVoice.play()
        }
    }

    changePlayerVoice(name: string) {
        let voice = this.gameAudioCache.getAudio(name)
        this.playerVoice && this.playerVoice.pause()
        if (voice) {
            this.playerVoice = voice
            this.playerVoice.play()
        }
    }

    playDoubleVoice(data: DoubleMsgType) {
        let { doubleType, needbroadcast } = data
        if (needbroadcast) {
            switch (doubleType) {
                case DoubleType.None:
                    this.changeDoubleVoice('bujiabei')
                    break;
                case DoubleType.Double:
                    this.changeDoubleVoice('jiabei')
                    break;
                case DoubleType.SuperDouble:
                    this.changeDoubleVoice('superjiabei')
                    break;
            }
        }
    }

    sangeVoiceHandler(compareNum: number) {
        let voice = this.gameAudioCache.getAudio('sange')
        this.playerVoice && this.playerVoice.pause()
        if (voice) {
            this.playerVoice = voice
            this.playerVoice.play()
            this.playerVoice.onended = () => {
                this.changePlayerVoice(compareNum.toString())
            }
        }
    }

    playPlayerVoice() {
        let outCardInfo = this.model.lastOutCardInfo?.outCardInfo;
        let voiceName = '';
        if (!outCardInfo) { // 不要
            voiceName = 'buyao' + Util.randomInt(4)
            this.changePlayerVoice(voiceName)
            return;
        }
        let type = outCardInfo.type;
        let compareNum = outCardInfo.compareNum;
        this.changeSpecialVoice('chupai', this.specialVoice2)
        switch (type) {
            case OutCardType.Single:
                voiceName = compareNum.toString()
                break;
            case OutCardType.Double:
                voiceName = `dui${compareNum.toString()}`
                break;
            case OutCardType.Three:
                // 特殊处理, 需要拼接声音
                this.sangeVoiceHandler(compareNum)
                return;
            case OutCardType.ThreeWithOne:
                voiceName = 'sandaiyi'
                break;
            case OutCardType.ThreeWithTwo:
                voiceName = 'sandaiyidui'
                break;
            case OutCardType.FourWithTwo:
                voiceName = 'sidaier'
                break;
            case OutCardType.FourWithFour:
                voiceName = 'sidailiangdui'
                break;
            case OutCardType.Shunzi:
                voiceName = 'shunzi'
                this.changeSpecialVoice('flower', this.specialVoice1)
                break;
            case OutCardType.ConnectedPair:
                voiceName = 'liandui'
                this.changeSpecialVoice('flower', this.specialVoice1)
                break;
            case OutCardType.AirFly:
                voiceName = 'feiji'
                this.changeSpecialVoice('plane', this.specialVoice1)
                break;
            case OutCardType.Bomb:
                voiceName = 'zhadan'
                this.changeSpecialVoice('boom', this.specialVoice1)
                break;
            case OutCardType.KingBomb:
                voiceName = 'wangzha'
                this.changeSpecialVoice('boom', this.specialVoice1)
                break;
            default:
                break;
        }
        this.changePlayerVoice(voiceName)
    }
    checkBGM(summaryObj?: SummaryObjType) {
        let gameStatus = this.model.gameStatus
        let bgmName = 'welcome'
        if (gameStatus === GameStatus.Gaming) {
            let cardNum = this.model.cardNum
            bgmName = 'normal'
            let minNum = 100
            for (const username in cardNum) {
                minNum = Math.min(cardNum[username], minNum)
            }
            if (minNum <= 10) {
                bgmName = 'normal2'
            } else if (minNum <= 5) {
                bgmName = 'exciting'
            }
        }
        if (summaryObj) {
            let summary = summaryObj[this.model.myname];
            let iswin = summary.isWin;
            bgmName = iswin ? 'win' : 'lose';
        }
        if (bgmName !== this.bgmName) {
            this.bgmName = bgmName
            this.changeBgm(this.bgmName);
        }
    }

    timerAlert = () => {
        this.changeSpecialVoice('timer', this.specialVoice3)
    }

    cardNumberAlert(cardNum: number) {
        if (cardNum === 2) {
            this.changeSpecialVoice('warning', this.specialVoice2)
            this.changeSpecialVoice('baojing2', this.specialVoice3)
        } else if (cardNum === 1) {
            this.changeSpecialVoice('warning', this.specialVoice2)
            this.changeSpecialVoice('baojing1', this.specialVoice3)
        }

    }

    notify = (caster: Broadcaster, event: EventType): void => {
        let action = event.action
        switch (action) {
            case DataChange.FinishLoad:
                this.checkBGM()
                if (this.model.gameStatus === GameStatus.Gaming) {
                    this.changeSpecialVoice('fapai', this.specialVoice1)
                }
                break;
            case DataChange.GameStart:
                this.checkBGM()
                this.changeSpecialVoice('fapai', this.specialVoice2)
                break;
            case DataChange.OutCard:
                this.playPlayerVoice()
                this.checkBGM()
                !event.data.notBroad && this.cardNumberAlert(event.data.cardNum)
                break;
            case DataChange.Double:
                this.playDoubleVoice(event.data)
                break;
            case DataChange.MySelfOutCard:
                this.playPlayerVoice()
                !event.data.notBroad && this.cardNumberAlert(event.data.cardNum)
                this.checkBGM()
                break;
            case DataChange.GameOver:
                this.checkBGM(event.data)
                break;
            case DataChange.Restart:
                this.changeBgm('welcome')
                break;
            default:
                break;
        }
    }

    unMounted() {
        this.bgm && this.bgm.pause()
    }
}